package com.wallace.downloadserver.util.myEncrypt

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.crypto.*
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.random.Random

/**
 * <Description> AES加密工具类
 *
 * @author Wallace
 * @Date 2021/11/25 14:13
 */
object AESCryptUtil {

    private val log: Logger = LoggerFactory.getLogger(AESCryptUtil::class.java)

    const val AES_ALGORITHM = "AES"

    //algorithm/mode/padding
    private const val AES_TRANSFORMATION = "AES/CBC/PKCS5Padding"

    const val SHORT_LENGTH = 128
    const val MIDDLE_LENGTH = 256
    const val LONG_LENGTH = 512

    /**
     * 随机生成SecretKeySpec
     * @return AES密钥
     */
    fun generateKey(length: Int = MIDDLE_LENGTH): AESKey {
        val generator = KeyGenerator.getInstance(AES_ALGORITHM)
        generator.init(length)
        val secretKeySpec = generator.generateKey() as SecretKeySpec
        return AESKey(secretKeySpec.encoded, getRandomInitialVector())
    }

    /**
     * 随机生成16个ASCII码在32~126之间的字符，组成16字节的初始偏移向量
     * @return 偏移向量
     */
    private fun getRandomInitialVector(): ByteArray {
        val iv = ByteArray(16)
        val random = Random(System.currentTimeMillis())
        for (index in iv.indices) {
            iv[index] = (32..126).random(random).toByte()
        }
        return iv
    }

    /**
     * 加密
     * @param plainByteArray 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return ByteArray 密文
     */
    /**
     * 加密
     * @param plainByteArray 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return ByteArray 密文
     */
    fun encrypt(plainByteArray: ByteArray, key: SecretKeySpec, iv: ByteArray): ByteArray? {
        var output: ByteArray? = null
        try {
            val cipher = Cipher.getInstance(AES_TRANSFORMATION)
            cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
            output = cipher.doFinal(plainByteArray)
        } catch (e: NoSuchAlgorithmException) {
            log.error("$e")
        } catch (e: NoSuchPaddingException) {
            log.error("$e")
        } catch (e: InvalidKeyException) {
            log.error("$e")
        } catch (e: InvalidAlgorithmParameterException) {
            log.error("$e")
        } catch (e: IllegalBlockSizeException) {
            log.error("$e")
        } catch (e: BadPaddingException) {
            log.error("$e")
        } finally {
            return output
        }
    }

    /**
     * 加密
     * @param plainText 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return String（UTF-8） 密文
     */
    fun encryptToString(plainText: String, key: SecretKeySpec, iv: ByteArray): String? {
        val cipherByteArray = encrypt(plainText.toByteArray(), key, iv)
        return cipherByteArray?.toString(Charsets.UTF_8)
    }

    /**
     * 先加密后编码
     * @param plainByteArray 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return ByteArray Base64编码后的密文
     */
    fun encryptAndEncode(plainByteArray: ByteArray, key: SecretKeySpec, iv: ByteArray): ByteArray? {
        var cipherByteArray = encrypt(plainByteArray, key, iv)
        if (cipherByteArray != null) {
            val encoder = Base64.getEncoder()
            cipherByteArray = encoder.encode(cipherByteArray)
        }
        return cipherByteArray
    }

    /**
     * 先加密后编码
     * @param plainText 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return String（UTF-8） 密文
     */
    fun encryptAndEncodeToString(plainText: String, key: SecretKeySpec, iv: ByteArray): String? {
        val cipherByteArray = encryptAndEncode(plainText.toByteArray(), key, iv)
        return cipherByteArray?.toString(Charsets.UTF_8)
    }

    /**
     * 解密
     * @param cipherByteArray 明文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return ByteArray 明文
     */
    fun decrypt(cipherByteArray: ByteArray, key: SecretKeySpec, iv: ByteArray): ByteArray? {
        var output: ByteArray? = null
        try {
            val cipher = Cipher.getInstance(AES_TRANSFORMATION)
            cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))
            output = cipher.doFinal(cipherByteArray)
        } catch (e: NoSuchAlgorithmException) {
            log.error("$e")
        } catch (e: NoSuchPaddingException) {
            log.error("$e")
        } catch (e: InvalidKeyException) {
            log.error("$e")
        } catch (e: InvalidAlgorithmParameterException) {
            log.error("$e")
        } catch (e: IllegalBlockSizeException) {
            log.error("$e")
        } catch (e: BadPaddingException) {
            log.error("$e")
        } finally {
            return output
        }
    }

    /**
     * 解密
     * @param cipherText 密文
     * @param key 加密密钥
     * @param iv 偏移向量
     * @return String（UTF-8） 明文
     */
    fun decryptToString(cipherText: String, key: SecretKeySpec, iv: ByteArray): String? {
        val plaintByteArray = decrypt(cipherText.toByteArray(), key, iv)
        return plaintByteArray?.toString(Charsets.UTF_8)
    }

    /**
     * 先解码后解密
     * @param cipherByteArray Base64编码后的密文
     * @param key 解密密钥
     * @param iv 偏移向量
     * @return ByteArray 明文
     */
    fun decryptAfterDecode(
            cipherByteArray: ByteArray,
            key: SecretKeySpec,
            iv: ByteArray
    ): ByteArray? {
        val decoder = Base64.getDecoder()
        val cipherDecodeByteArray = decoder.decode(cipherByteArray)
        return decrypt(cipherDecodeByteArray, key, iv)
    }

    /**
     * 先解码后解密
     * @param cipherTextEncoded Base64编码后的密文
     * @param key 解密密钥
     * @param iv 偏移向量
     * @return String（UTF-8）明文
     */
    fun decryptAfterDecodeToString(
            cipherTextEncoded: String,
            key: SecretKeySpec,
            iv: ByteArray
    ): String? {
        val plainTextByteArray = decryptAfterDecode(cipherTextEncoded.toByteArray(), key, iv)
        return plainTextByteArray?.toString(Charsets.UTF_8)
    }
}